module mculib.baremetal.volatile.atomic;


import core.atomic;
import core.volatile;

import std.traits:isScalarType;

import mculib.baremetal.volatile.misc:StringToInt;

/**
    内存地址访问 `Atomic` 操作模板
    索引读访问 `boot value = [index]`,
    索引写访问 `[index] = false`,
    索引写访问 `[index] |= false`,
    索引写访问 `[index] ^= false`,
    索引写访问 `[index] &= false`,
    重载
    X? = bit
    B? = byte
    W? = word
    D? = dword
    Example:
    ---
    import mculib.baremetal.volatile;

    VolatileBitBand!uint value;
    auto pValue = cast(VolatileBitBand!uint*)0x40000000;

    value[0] = true;
    assert(*cast(uint*)&value == 0x01);
    assert(value[0] == true);
    value[1] = true;
    assert(*cast(uint*)&value == 0x03);
    ---
*/
struct volatile(T)
if(isScalarType!T)
{
    private
    union{
        T _value;
        shared(T) _sharedValue;
    }

    //@disable this();
    //@disable opCall(Args...)(Args args);
    @property T* ptr() => (&_value);
    
    @property T value() const => atomicLoad(_sharedValue);

    @property void value(T v) => atomicStore(_sharedValue,v);

    @property size_t opDollar(size_t pos)() const
        => (T.sizeof * 8);
        
    /// 重载赋值
    void opAssign(T v) => atomicStore(_sharedValue,v);

    void opAssign(ref typeof(this) v)
    {
        atomicStore(_sharedValue,v.value());
    }

    /// 重载 cast 转换
    T opCast() const => atomicLoad(_sharedValue);
    /// 重载比较
    bool opEquals(T v) const => atomicLoad(_sharedValue) == v;
    bool opEquals(ref typeof(this) v) const => atomicLoad(_sharedValue) == v.value();

    /// 重载索引读取
    bool opIndex(size_t index) const
    {
        return (atomicLoad(_sharedValue) & (1 << index)) != 0;
    }
    /// 重载索引写入 [index] = bool 置位/清除位
    bool opIndexAssign(bool v,size_t index)
    {
        T set = 1u << index;
        if(v)
        {
            return (atomicOp!("|=")(_sharedValue,set) & set ) == set;
        }
        else
        {
            return  (atomicOp!("&=")(_sharedValue,~set) & set) == set;
        }
    }
    /// 重载索引位运算 `^=`
    bool opIndexOpAssign(string op:"^")(bool v,size_t index) const 
    {
        auto set = 1u << index;
        if(v)
        {
            return (atomicOp!("^=")(_sharedValue,set) == set);
        }
        else 
        {
            return (atomicOp!("^=")(_sharedValue,~set) == set);
        }
    }
    /// 重载位运算所有运算符
    T opOpAssign(string op)(T value)
    {
        return atomicOp!(op~"=")(_sharedValue,value);
    }

    /**
        以位掩码做参考,写入值,返回修改后的值
        Example:
        ---
        import mculib.bareband.volatile;

        VolatileAtomic!uint value;
        auto pValue = cast(VolatileAtomic!uint*)0x40000000;

        value.maskWrite(0x01,0x02);
        assert(*cast(uint*)&value == 0x01);
        ---
    */
    /*
    T maskWrite(T mask,T value)
    {
        T mset, mget = atomicLoad(_sharedValue);
        do{
            mset = (mget & ~mask) | (value & mask);
        }while (!casWeak(&_sharedValue, mget, mset));
        return mget;
    }
    */
    /**
        重载
        X? = bit
        B? = byte
        W? = word
        D? = dword
    */
    @property
    auto opDispatch(string name)() const
    if((name.length>1) && ((name[0]=='X') || (name[0]=='B')||(name[0]=='W')||(name[0]=='D')))
    {
        enum pos = StringToInt(name[1..$]);
        static assert(pos >=0,"Invalid position");
        static if(name[0] == 'X')
        {
            static assert(pos <= T.sizeof * 8,"Invalid position");
            return this[pos];
        }
        else static if(name[0] == 'B') 
        {
            static assert((pos * ubyte.sizeof)  <= T.sizeof ,"Invalid position");
            auto pval = cast(ubyte*)&(this._value);
            return volatileLoad(&pval[pos]);
        }
        else static if(name[0] == 'W') 
        {
            static assert(pos * ushort.sizeof <= T.sizeof ,"Invalid position");
            auto pval = cast(ushort*)&(this._value);
            return volatileLoad(&pval[pos]);
        }
        else static if(name[0] == 'D') 
        {
            static assert(pos * uint.sizeof <= T.sizeof ,"Invalid position");
            auto pval = cast(uint*)&(this._value);
            return volatileLoad(&pval[pos]);
        }
        else 
            static assert(false,"Invalid position");
    }

    @property
    void opDispatch(string name)(T v)
    if((name.length>1) && ((name[0]=='X') || (name[0]=='B')||(name[0]=='W')||(name[0]=='D')))
    {
        
        enum pos = StringToInt(name[1..$]);
        static assert(pos >=0,"Invalid position");
        
        static if(name[0] == 'X')
        {
            static assert(pos <= T.sizeof * 8,"Invalid position");
            this[pos] = cast(bool)v;
        }
        else static if(name[0] == 'B') 
        {
            static assert((pos * ubyte.sizeof)  <= T.sizeof ,"Invalid position");
            auto pval = cast(ubyte*)&(this._value);
            volatileStore(&pval[pos],cast(ubyte)v);
        }
        else static if(name[0] == 'W') 
        {
            static assert(pos * ushort.sizeof <= T.sizeof ,"Invalid position");
            auto pval = cast(ushort*)&(this._value);
            volatileStore(&pval[pos],cast(ushort)v);
        }
        else static if(name[0] == 'D') 
        {
            static assert(pos * uint.sizeof <= T.sizeof ,"Invalid position");
            auto pval = cast(uint*)&(this._value);
            volatileStore(&pval[pos],cast(uint)v);
        }
        else 
            static assert(false,"Invalid position");
    }
}

